package com.mars.common.repository;


import com.google.common.collect.Iterables;
import com.mars.common.convert.IEntityPOConvert;
import com.mars.common.utils.Beans;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.rp.spring.jpa.GenericJpaRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;

import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public abstract class SimplePoConvertEntityRepository<E, T, ID extends Serializable> {

	List<E> transform(List<T> l) {
//		return Lists.transform(l, t -> convert().toEntity(t));
		return l.stream().map(t -> convert().toEntity(t)).collect(Collectors.toList());
	}
	
	public List<E> findAll() {
		List<T> list = repo().findAll();
		return transform(list);
	}

	public List<E> findAll(Sort sort) {
		List<T> list = repo().findAll(sort);
		return transform(list);
	}

	public List<E> findAllById(Iterable<ID> ids) {
		List<T> list = repo().findAllById(ids);
		return transform(list);
	}

//	public void saveAll(Iterable<E> entities) {
//		repository().saveAll(Iterables.transform(entities, e -> convert().toPo(e)));
//	}

	public void deleteInBatch(Iterable<E> entities) {
		repo().deleteInBatch(Iterables.transform(entities, e -> convert().toPo(e)));
	}

	public E getOne(ID id) {
		T t = repo().getOne(id);
		return convert().toEntity(t);
	}

	public E save(E entity) {
		T po = convert().toPo(entity);
		repo().save(po);
		E newEntity = convert().toEntity(po);
		Beans.copyProperties(newEntity, entity);
		return newEntity;
	}
	
	public List<E> saveAll(Iterable<E> entities) {
		Iterable<T> iterable = Iterables.transform(entities, e -> convert().toPo(e));
		List<T> list = repo().saveAll(iterable);
		return transform(list);
	}

	public Optional<E> findById(ID id) {
		Optional<T> optional = repo().findById(id);
		return Optional.ofNullable(convert().toEntity(optional.orElse(null)));
	}

	public boolean existsById(ID id) {
		return repo().existsById(id);
	}

	public long count() {
		return repo().count();
	}

	public void deleteById(ID id) {
		repo().deleteById(id);
	}

	public void delete(E e) {
		repo().delete(convert().toPo(e));
	}

	public void deleteAll(Iterable<? extends E> entities) {
		repo().deleteAll(Iterables.transform(entities, e -> convert().toPo(e)));
	}

	public Optional<E> findOne(Predicate predicate) {
		Optional<T> optional = repo().findOne(predicate);
		return Optional.ofNullable(convert().toEntity(optional.orElse(null)));
	}

	public long count(Predicate predicate) {
		return repo().count(predicate);
	}

	public boolean exists(Predicate predicate) {
		return repo().exists(predicate);
	}

	public List<E> findAll(Predicate predicate, int maxResult) {
		List<T> list = repo().findAll(predicate, maxResult);
		return transform(list);
	}

	public List<E> findAll(Predicate predicate) {
		List<T> list = repo().findAll(predicate);
		return transform(list);
	}

	public List<E> findAll(Predicate... predicate) {
		List<T> list = repo().findAll(predicate);
		return transform(list);
	}

	public List<E> findAll(Predicate predicate, Sort sort) {
		List<T> list = repo().findAll(predicate, sort);
		return transform(list);
	}

	public List<E> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
		List<T> list = repo().findAll(predicate, orders);
		return transform(list);
	}

	public List<E> findAll(OrderSpecifier<?>... orders) {
		List<T> list = repo().findAll(orders);
		return transform(list);
	}

	public long count(Predicate... predicate) {
		return repo().count(predicate);
	}

	public Page<E> findAll(Predicate predicate, Pageable pageable) {
		Page<T> page = repo().findAll(predicate, pageable);
		Page<E> map = page.map(t -> convert().toEntity(t));
		return map;
	}
	
	public Page<E> findAll(Pageable pageable) {
		Page<T> page = repo().findAll(pageable);
		Page<E> map = page.map(t -> convert().toEntity(t));
		return map;
	}

	public Page<E> findAll(Predicate predicate, Pageable pageable, OrderSpecifier<?>... sorts) {
		Page<T> page = repo().findAll(predicate, pageable, sorts);
		Page<E> map = page.map(t -> convert().toEntity(t));
		return map;
	}

	public E findOneIfMutil(Predicate... predicate) {
		T t = repo().findOneIfMutil(predicate);
		return convert().toEntity(t);
	}
	
	public void setStatusEnable(ID id) {
		repo().setStatusEnable(id);
	}
	
	/**
	 * 设置实体.status的状态为 禁用 状态
	 */
	public void setStatusDisable(ID id) {
		repo().setStatusDisable(id);
	}
	
	public void setStatusValue(ID id, Integer status) {
		repo().setStatusValue(id, status);
	}
	
	//~==================================================== JpaSpecificationExecutor 实现


	/**
	 * Returns a single entity matching the given {@link Specification} or {@link Optional#empty()} if none found.
	 *
	 * @param spec can be {@literal null}.
	 * @return never {@literal null}.
	 * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found.
	 */
	public Optional<E> findOne(@Nullable Specification<T> spec) {
		Optional<T> optional = repo().findOne(spec);
		return Optional.ofNullable(convert().toEntity(optional.orElse(null)));
	}

	/**
	 * Returns all entities matching the given {@link Specification}.
	 *
	 * @param spec can be {@literal null}.
	 * @return never {@literal null}.
	 */
	public List<E> findAll(@Nullable Specification<T> spec) {
		List<T> list = repo().findAll(spec);
		return transform(list);
	}

	/**
	 * Returns a {@link Page} of entities matching the given {@link Specification}.
	 *
	 * @param spec can be {@literal null}.
	 * @param pageable must not be {@literal null}.
	 * @return never {@literal null}.
	 */
	public Page<E> findAll(@Nullable Specification<T> spec, Pageable pageable) {
		Page<T> page = repo().findAll(spec, pageable);
		Page<E> map = page.map(t -> convert().toEntity(t));
		return map;
	}

	/**
	 * Returns all entities matching the given {@link Specification} and {@link Sort}.
	 *
	 * @param spec can be {@literal null}.
	 * @param sort must not be {@literal null}.
	 * @return never {@literal null}.
	 */
	public List<E> findAll(@Nullable Specification<T> spec, Sort sort) {
		List<T> list = repo().findAll(spec, sort);
		return transform(list);
	}

	/**
	 * Returns the number of instances that the given {@link Specification} will return.
	 *
	 * @param spec the {@link Specification} to count instances for. Can be {@literal null}.
	 * @return the number of instances.
	 */
	public long count(@Nullable Specification<T> spec) {
		return repo().count(spec);
	}

	public abstract GenericJpaRepository<T, ID> repo();
	public abstract IEntityPOConvert<E, T> convert();
}
